using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

using XBLIGUK2011Talk2.PostProcessing;
using XBLIGUK2011Talk2.HUD;

namespace XBLIGUK2011Talk2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Base3DCamera camera;

        RenderTarget2D depthBuffer;
        RenderTarget2D backBuffer;

        PostProcessingManager ppManager;
        SunEffect sun;
        DepthOfFieldEffect dof;
        BloomEffect bloom;
        HeatHazeEffect haze;
        RadialBlurEffect radialBlur;
        RippleEffect ripple;
        FogEffect fog;
        CrepuscularRaysEffect rays;

        SkyBox skyBox;

        Base3DObject cube;
        Base3DObject[] bigShip;
        Base3DObject landShark;
        Base3DObject earth;

        BasicHUD hud;

        HUDSlider sldrBloomInt;
        HUDSlider sldrBloomBlur;

        HUDSlider sldrDofFocalDepth;
        HUDSlider sldrDofFocalRange;

        HUDSlider sldrFogDepth;
        HUDSlider sldrFogRange;

        Vector3 LightPosition = new Vector3(50, 50, 1000);

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 800;
            graphics.IsFullScreen = false;

            IsMouseVisible = true;

            camera = new Base3DCamera(this, .5f, 20000);
            camera.Position = new Vector3(0, 0, 0);
            Components.Add(camera);
            Services.AddService(typeof(Base3DCamera), camera);

            //skyBox = new SkyBox(this, "Textures/HMcubemap");
            skyBox = new SkyBox(this, "Textures/SkyBox/NebulaBlue");
            Components.Add(skyBox);

            cube = new Base3DObject(this, "Models/cube");
            cube.Position = new Vector3(0, 0, -10);
            cube.ColorAsset = "Textures/h2mcpCube";
            cube.BumpAsset = "Textures/h2mcpCubeNormal";
            cube.LightPosition = LightPosition;
            Components.Add(cube);

            bigShip = new Base3DObject[10];
            for (int s = 0; s < 10; s++)
            {
                bigShip[s] = new Base3DObject(this, "Models/bigship1");
                bigShip[s].Position = new Vector3(20 + (s * 12), 0, -10 - (s * 12));
                bigShip[s].ColorAsset = "Textures/WindmillTopColor";
                bigShip[s].BumpAsset = "Textures/WindmillTopNormal";
                bigShip[s].LightPosition = LightPosition;
                Components.Add(bigShip[s]);
            }

            landShark = new Base3DObject(this, "Models/landShark");
            landShark.Position = new Vector3(-20, 0, -10);
            landShark.ColorAsset = "Textures/Speeder_diff";
            landShark.BumpAsset = "Textures/Speeder_bump";
            landShark.LightPosition = LightPosition;
            Components.Add(landShark);

            earth = new Base3DObject(this, "Models/sphere");
            earth.Position = new Vector3(0, 0, -200);
            earth.ColorAsset = "Textures/Earth_Diffuse";
            earth.BumpAsset = "Textures/Earth_NormalMap";
            earth.LightPosition = LightPosition;
            Components.Add(earth);

            ppManager = new PostProcessingManager(this);

            fog = new FogEffect(this, 50, 100, Color.DarkSlateGray);
            fog.Enabled = false;
            ppManager.AddEffect(fog);

            /* original sun shader */
            //sun = new SunEffect(this, LightPosition);
            //ppManager.AddEffect(sun);

            /* new crepuscular rays effect */
            rays = new CrepuscularRaysEffect(this, LightPosition);
            rays.Enabled = true;
            ppManager.AddEffect(rays);
            
            dof = new DepthOfFieldEffect(this, 5, 30);
            dof.Enabled = false;
            ppManager.AddEffect(dof);

            bloom = new BloomEffect(this, 1.25f, 1f, 1f, 1f, .25f, 4f);
            bloom.Enabled = false;
            ppManager.AddEffect(bloom);

            haze = new HeatHazeEffect(this,"Textures/bumpmap", false);
            haze.Enabled = false;
            ppManager.AddEffect(haze);

            radialBlur = new RadialBlurEffect(this, 0.009f);
            radialBlur.Enabled = false;
            ppManager.AddEffect(radialBlur);

            ripple = new RippleEffect(this);
            ripple.Enabled = false;
            ppManager.AddEffect(ripple);

            hud = new BasicHUD(this,240,800);
            Components.Add(hud);

            HUDCheckBox chkBxBloom = new HUDCheckBox("chkBloom", "Bloom On/Off", 16, 16);
            chkBxBloom.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxBloom);

            HUDCheckBox chkBxDof = new HUDCheckBox( "chkDof", "Depth of Field On/Off", 64, 16);
            chkBxDof.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxDof);

            HUDCheckBox chkBxHaze = new HUDCheckBox( "chkHaze", "Haze On/Off", 160, 256);
            chkBxHaze.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxHaze);

            HUDCheckBox chkBxRadBlur = new HUDCheckBox( "chkRadBlur", "Radial Blur On/Off", 160, 16);
            chkBxRadBlur.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxRadBlur);

            HUDCheckBox chkBxRipple = new HUDCheckBox( "chkBxRipple", "Ripple On/Off", 198, 16);
            chkBxRipple.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxRipple);

            HUDCheckBox chkBxFog = new HUDCheckBox("chkBxFog", "Fog On/Off",112, 16);
            chkBxFog.OnClick = new OnClickEvent(Checked);
            hud.AddItem(chkBxFog);

            sldrBloomInt = new HUDSlider( "sldBloomInt", "Bloom Intensity", 8, 256, 0, 3);
            sldrBloomInt.Value = bloom.BloomIntensity;
            sldrBloomInt.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrBloomInt);

            sldrBloomBlur = new HUDSlider( "sldBloomBlur", "Bloom Blur", 8, 512, 0, 8);
            sldrBloomBlur.Value = bloom.BlurAmount;
            sldrBloomBlur.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrBloomBlur);

            sldrDofFocalDepth = new HUDSlider( "sldrDofFocalDepth", "Focal Distance", 58, 256, 0, 200);
            sldrDofFocalDepth.Value = dof.FocalDistance;
            sldrDofFocalDepth.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrDofFocalDepth);

            sldrDofFocalRange = new HUDSlider("sldrDofFocalRange", "Focal Range", 58, 512, 0, 200);
            sldrDofFocalRange.Value = dof.FocalRange;
            sldrDofFocalRange.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrDofFocalRange);

            sldrFogDepth = new HUDSlider("sldrFogDepth", "Focal Distance", 108, 256, 0, 200);
            sldrFogDepth.Value = fog.FogDistance;
            sldrFogDepth.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrFogDepth);

            sldrFogRange = new HUDSlider("sldrFogRange", "Fog Range", 108, 512, 0, 200);
            sldrFogRange.Value = fog.FogRange;
            sldrFogRange.OnChange = new OnChangeEvent(sliderChange);
            hud.AddItem(sldrFogRange);

            HUDButton btnExit = new HUDButton("btnExit", "Close", 190, 700);
            btnExit.OnClick = new OnClickEvent(btnClick);
            hud.AddItem(btnExit);
        }


        void btnClick(object sender)
        {
            switch (((HUDButton)sender).Name)
            {
                case "btnExit":
                    if (hud.CurrentState != BasicHUD.States.Hidden)
                        hud.CurrentState = BasicHUD.States.Hiding;
                    break;
            }
        }
        void sliderChange(object sender)
        {
            float val = ((HUDSlider)sender).Value;
            switch (((HUDSlider)sender).Name)
            {
                case "sldBloomInt":
                    bloom.BloomIntensity = val;
                    break;
                case "sldBloomBlur":
                    bloom.BlurAmount = val;
                    break;
                case "sldrDofFocalDepth":
                    dof.FocalDistance = val;
                    break;
                case "sldrDofFocalRange":
                    dof.FocalRange = val;
                    break;
                case "sldrFogDepth":
                    fog.FogDistance = val;
                    break;
                case "sldrFogRange":
                    fog.FogRange = val;
                    break;
            }
        }
        void Checked(object sender)
        {
            switch (((HUDCheckBox)sender).CurrentState)
            {
                case HUDCheckBox.States.Checked:
                    ((HUDCheckBox)sender).CurrentState = HUDCheckBox.States.UnChecked;
                    break;
                case HUDCheckBox.States.UnChecked:
                    ((HUDCheckBox)sender).CurrentState = HUDCheckBox.States.Checked;
                    break;
            }
            switch (((HUDCheckBox)sender).Name)
            {
                case "chkBloom":
                    bloom.Enabled = !bloom.Enabled;
                    break;
                case "chkDof":
                    dof.Enabled = !dof.Enabled;
                    break;
                case "chkHaze":
                    haze.Enabled = !haze.Enabled;
                    break;
                case "chkRadBlur":
                    radialBlur.Enabled = !radialBlur.Enabled;
                    break;
                case "chkBxRipple":
                    ripple.Enabled = !ripple.Enabled;
                    break;
                case "chkBxFog":
                    fog.Enabled = !fog.Enabled;
                    break;
            }
        }

        

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Services.AddService(typeof(SpriteBatch), spriteBatch);

            backBuffer = new RenderTarget2D(GraphicsDevice,
                                            GraphicsDevice.Viewport.Width,
                                            GraphicsDevice.Viewport.Height,
                                            false,
                                            SurfaceFormat.Color,
                                            DepthFormat.Depth16);

            depthBuffer = new RenderTarget2D(GraphicsDevice,
                                            GraphicsDevice.Viewport.Width,
                                            GraphicsDevice.Viewport.Height,
                                            false, SurfaceFormat.Single,
                                            DepthFormat.Depth16);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here            
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape) || GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            earth.Rotate(Vector3.Up, .0001f);

            // Camera controls..
            float speedTran = .1f;
            float speedRot = .01f;

            KeyboardState thisKBState = Keyboard.GetState();

            if (thisKBState.IsKeyDown(Keys.W) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y > 0)
                camera.Translate(Vector3.Forward * speedTran);
            if (thisKBState.IsKeyDown(Keys.S) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y < 0)
                camera.Translate(Vector3.Backward * speedTran);
            if (thisKBState.IsKeyDown(Keys.A) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X < 0)
                camera.Translate(Vector3.Left * speedTran);
            if (thisKBState.IsKeyDown(Keys.D) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X > 0)
                camera.Translate(Vector3.Right * speedTran);

            if (thisKBState.IsKeyDown(Keys.Left) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X < 0)
                camera.Rotate(Vector3.Up, speedRot);
            if (thisKBState.IsKeyDown(Keys.Right) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X > 0)
                camera.Rotate(Vector3.Up, -speedRot);
            if (thisKBState.IsKeyDown(Keys.Up) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y > 0)
                camera.Rotate(Vector3.Right, speedRot);
            if (thisKBState.IsKeyDown(Keys.Down) || GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y < 0)
                camera.Rotate(Vector3.Right, -speedRot);

            if (GamePad.GetState(PlayerIndex.One).Buttons.B == ButtonState.Pressed)
                bloom.Enabled = true;
            if (GamePad.GetState(PlayerIndex.One).Buttons.X == ButtonState.Pressed)
                bloom.Enabled = false;

            if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
                dof.Enabled = true;
            if (GamePad.GetState(PlayerIndex.One).Buttons.Y == ButtonState.Pressed)
                dof.Enabled = false;


            if (Keyboard.GetState().IsKeyDown(Keys.F1))
            {
                if(hud.CurrentState != BasicHUD.States.Shown)
                    hud.CurrentState = BasicHUD.States.Showing;
            }
            
            if (Keyboard.GetState().IsKeyDown(Keys.R))
                sldrBloomInt.Value += .05f;
            if (Keyboard.GetState().IsKeyDown(Keys.L))
                sldrBloomInt.Value -= .05f;
                
            //if (Keyboard.GetState().IsKeyDown(Keys.F1))
            //    dof.FocalDistance+=1f;
            //if (Keyboard.GetState().IsKeyDown(Keys.F2))
            //    dof.FocalDistance-=1f;
            //if (Keyboard.GetState().IsKeyDown(Keys.F3))
            //    dof.FocalRange+=1f;
            //if (Keyboard.GetState().IsKeyDown(Keys.F4))
            //    dof.FocalRange-=1f;

            //if (Keyboard.GetState().IsKeyDown(Keys.F5))
            //    dof.Enabled = false;
            //if (Keyboard.GetState().IsKeyDown(Keys.F6))
            //    dof.Enabled = true;

            //if (Keyboard.GetState().IsKeyDown(Keys.F7))
            //    bloom.Enabled = false;
            //if (Keyboard.GetState().IsKeyDown(Keys.F8))
            //    bloom.Enabled = true;

            //if (Keyboard.GetState().IsKeyDown(Keys.F9))
            //    haze.Enabled = false;
            //if (Keyboard.GetState().IsKeyDown(Keys.F10))
            //    haze.Enabled = true;

            //if (Keyboard.GetState().IsKeyDown(Keys.F11))
            //    radialBlur.Enabled = false;
            //if (Keyboard.GetState().IsKeyDown(Keys.F12))
            //    radialBlur.Enabled = true;


            //Window.Title = string.Format("D: {2} FD: {0} FR: {1}", dof.FocalDistance, dof.FocalRange,Vector3.Distance(camera.Position,bigShip[0].Position));

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // Set up RT's
            GraphicsDevice.SetRenderTargets(backBuffer, depthBuffer);
            GraphicsDevice.Clear(Color.CornflowerBlue);

            base.Draw(gameTime);

            //Resolve targets.
            GraphicsDevice.SetRenderTarget(null);

            ppManager.Draw(gameTime, backBuffer, depthBuffer);

            // Draw the HUD
            hud.Draw(gameTime);

            if (hud.CurrentState == BasicHUD.States.Hidden)
            {
                spriteBatch.Begin();
                spriteBatch.DrawString(Content.Load<SpriteFont>("Fonts/hudFont"), "Hit F1 for HUD", new Vector2(8, 8), Color.Gold);
                spriteBatch.End();
            }
        }
        public void SaveJpg(Texture2D texture, string name)
        {
            FileStream s = new FileStream(name, FileMode.Create);
            texture.SaveAsJpeg(s, texture.Width, texture.Height);
            s.Close();
        }
    }
}
